using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using FoundationLib.Constraints;


namespace FoundationLib.Constraints 
{
	/// <summary>
	/// Provides functionality for defining constraints
	/// </summary>
	public static class Constraint 
	{
		/// <summary>
		/// Checks the given invariant
		/// </summary>
		/// <param name="condition">The invariant</param>
		public static void Inv(bool condition)
		{
			if (!condition)
				throw new ConstraintViolatedException("Invariant violated!");
		}

		/// <summary>
		/// Defines a condition which must hold for all elements of a collection
		/// </summary>
		/// <returns>A boolean value denoting if the condition holds</returns>
		/// <param name="collection">The collection of elements</param>
		/// <param name="predicate">The predicate which must hold for all elements of the
		/// collection</param>
		public static bool ForAll<T>(IEnumerable<T> collection, Func<T, bool> predicate)
		{
			if (collection == null || predicate == null)
				return true;
			return collection.All(predicate);
		}

		/// <summary>
		/// Defines a condition which must hold for any item in a collection
		/// </summary>
		/// <returns>A boolean value denoting if the condition holds</returns>
		/// <param name="collection">The collection of elements</param>
		/// <param name="predicate">The predicate which must hold for any element in the
		/// collection</param>
		public static bool Exists<T>(IEnumerable<T> collection, Func<T, bool> predicate)
		{
			if (collection == null || predicate == null)
				return true;
			return collection.Any(predicate);
		}

		/// <summary>
		/// Checks the given invariant
		/// </summary>
		/// <param name="condition">The invariant</param>
		/// <param name="message">The message used if the condition does not hold</param>
		public static void Inv(bool condition, string message)
		{
			if (!condition)
				throw new ConstraintViolatedException(String.Format("Invariant violated: {0}", message));
		}

		/// <summary>
		/// Checks the given post-condition
		/// </summary>
		/// <param name="condition">The post-condition</param>
		public static void Post(bool condition)
		{
			if (!condition)
				throw new ConstraintViolatedException("Post-condition violated!");
		}

		/// <summary>
		/// Checks the given post-condition
		/// </summary>
		/// <param name="condition">The post-condition</param>
		/// <param name="message">The message used if the condition does not hold</param>
		public static void Post(bool condition, string message)
		{
			if (!condition)
				throw new ConstraintViolatedException(String.Format("Post-condition violated: {0}", message));
		}

		/// <summary>
		/// Checks the given pre-condition
		/// </summary>
		/// <param name="condition">The pre-condition</param>
		public static void Pre(bool condition)
		{
			if (!condition)
				throw new ConstraintViolatedException("Pre-condition violated!");
		}

		/// <summary>
		/// Checks the given pre-condition
		/// </summary>
		/// <param name="condition">The pre-condition</param>
		/// <param name="message">The message used if the condition does not hold</param>
		public static void Pre(bool condition, string message)
		{
			if (!condition)
				throw new ConstraintViolatedException(String.Format("Pre-condition violated: {0}", message));
		}

		/// <summary>
		/// Returns the old value of the given variable. Note: This method always returns
		/// the current value of the variable. Accessing old values of variables is only
		/// supported with Microsoft Code Contracts.
		/// </summary>
		/// <returns>This method always returns the current value; accessing old values
		/// without using Microsoft Code Contracts is not supported</returns>
		/// <param name="variable">The variable</param>
		public static T OldValue<T>(T variable)
		{
			return variable;
		}

		/// <summary>
		/// Returns the result of the method. Note: This method always returns the default
		/// of T. Accessing method result values is only supported when using Microsoft
		/// Code Contracts.
		/// </summary>
		/// <returns>The method result</returns>
		public static T Result<T>()
		{
			return default(T);
		}

	}

}